package oakclub.model;

import oakclub.entity.*;
import oakclub.global.Container;
import oakclub.jooq.Tables;
import oakclub.jooq.tables.*;
import oakclub.service.ActivityService;
import org.jooq.DSLContext;
import org.jooq.Record;
import org.jooq.Result;
import org.jooq.impl.DSL;

import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class ChatModel extends BaseModel
{
    public void addRoster(long userId, long targetId) throws Exception
    {
        this.execute(new IExecute()
        {
            @Override
            public void execute(DSLContext context, Object... params) throws Exception
            {
                // params
                long userId = (long) params[0];
                long targetId = (long) params[1];

                // table alias
                ChatRosters CR = Tables.CHAT_ROSTERS;

                // add roster
                Timestamp now = new Timestamp(System.currentTimeMillis());
                context
                    .insertInto(CR, CR.USER_ID, CR.TARGET_ID, CR.CREATED_TIME, CR.UNREAD_NUM, CR.LAST_MESSAGE, CR.LAST_MESSAGE_TIME, CR.LAST_ACTIVE_TIME)
                    .values(userId, targetId, now, (short) 0, "", (Timestamp) null, now)
                    .values(targetId, userId, now, (short) 0, "", (Timestamp) null, now)
                    .onDuplicateKeyIgnore()
                    .execute();
            }
        }, userId, targetId);
    }

    public RosterEntity getRoster(long userId, long targetId) throws Exception
    {
        return (RosterEntity) this.fetch(new IFetch()
        {
            @Override
            public Object fetch(DSLContext context, Object... params) throws Exception
            {
                // params
                long userId = (long) params[0];
                long targetId = (long) params[1];

                // table alias
                ChatRosters CR = Tables.CHAT_ROSTERS;

                // query
                Record record = context
                    .select(CR.UNREAD_NUM, CR.LAST_MESSAGE, CR.LAST_MESSAGE_TIME)
                    .from(CR)
                    .where(CR.USER_ID.eq(userId), CR.TARGET_ID.eq(targetId))
                    .limit(1)
                    .fetchOne();

                // parse
                RosterEntity roster = new RosterEntity();
                roster.setUnreadNum(record.getValue(CR.UNREAD_NUM));
                roster.setLastMessage(record.getValue(CR.LAST_MESSAGE));
                roster.setLastMessageTime(record.getValue(CR.LAST_MESSAGE_TIME));

                return roster;
            }
        }, userId, targetId);
    }

    public List<RosterEntity> getChatList(long userId) throws Exception
    {
        return (List<RosterEntity>) this.fetch(new IFetch()
        {
            @Override
            public Object fetch(DSLContext context, Object... params) throws Exception
            {
                // params
                long userId = (long) params[0];

                // table alias
                ChatRosters CR = Tables.CHAT_ROSTERS;
                ChatAccounts CA = Tables.CHAT_ACCOUNTS;
                UsersBlock UB = Tables.USERS_BLOCK;
                Users U = Tables.USERS;
                Likes L1 = Tables.LIKES.as("l1");
                Likes L2 = Tables.LIKES.as("l2");
                NotchatMutualmatch NCMM = Tables.NOTCHAT_MUTUALMATCH;
                Vips V = Tables.VIPS;

                // query
                Result<Record> records = context
                    .select(CR.TARGET_ID, CR.UNREAD_NUM, CR.LAST_MESSAGE, CR.LAST_MESSAGE_TIME, CR.LAST_ACTIVE_TIME)
                    .select(U.NAME, U.AVATAR, U.BIRTHDAY)
                    .select(CA.USERNAME)
                    .select(DSL.decode().when(V.USER_ID.isNotNull(), 1).otherwise(0).as("is_vip"))
                    .select(DSL.decode().when(L1.USER_ID.isNotNull().and(L2.USER_ID.isNotNull()), 1).otherwise(0).as("is_matched"))
                    .select(DSL.greatest(L1.CREATED_TIME, L2.CREATED_TIME).as("matched_time"))
                    .select(DSL.decode().when(NCMM.USER_ID.isNotNull(), 1).otherwise(0).as("has_notchat_match"))
                    .from(CR)
                    .join(CA).on(CA.USER_ID.eq(CR.TARGET_ID))
                    .leftOuterJoin(UB).on(UB.USER_ID.eq(CR.USER_ID), UB.TARGET_ID.eq(CR.TARGET_ID))
                    .leftOuterJoin(U).on(U.USER_ID.eq(CR.TARGET_ID))
                    .leftOuterJoin(L1).on(L1.USER_ID.eq(CR.USER_ID), L1.TARGET_ID.eq(CR.TARGET_ID), L1.IS_LIKE.eq((short) 1))
                    .leftOuterJoin(L2).on(L2.USER_ID.eq(CR.TARGET_ID), L2.TARGET_ID.eq(CR.USER_ID), L2.IS_LIKE.eq((short) 1))
                    .leftOuterJoin(NCMM).on(NCMM.USER_ID.eq(CR.USER_ID), NCMM.TARGET_ID.eq(CR.TARGET_ID))
                    .leftOuterJoin(V).on(V.USER_ID.eq(CR.TARGET_ID), V.EXPIRE_DATE.gt(new Date().getTime()))
                    .where(CR.USER_ID.eq(userId), UB.USER_ID.isNull(), U.PROFILE_ID.isNotNull())
                    .orderBy(CR.LAST_ACTIVE_TIME.desc())
                    .fetch();

                // parse
                ActivityService activityService = (ActivityService) Container.getComponent(ActivityService.class);
                List<RosterEntity> rosters = new ArrayList<>();
                for(Record record : records)
                {
                    long targetId = (long) record.getValue(CR.TARGET_ID);

                    RosterEntity roster = new RosterEntity();
                    roster.setTarget(new UserEntity());
                    roster.setMutualMatch(new MutualMatchEntity());
                    roster.setLastMessage(record.getValue(CR.LAST_MESSAGE));
                    roster.setLastMessageTime(record.getValue(CR.LAST_MESSAGE_TIME));
                    roster.setUnreadNum(record.getValue(CR.UNREAD_NUM));
                    roster.setLastActiveTime(record.getValue(CR.LAST_ACTIVE_TIME));
                    rosters.add(roster);

                    UserEntity target = roster.getTarget();
                    target.setId(targetId);
                    target.setName(record.getValue(U.NAME));
                    target.setAvatar(new PhotoEntity(target, record.getValue(U.AVATAR)));
                    target.setBirthday(record.getValue(U.BIRTHDAY));
                    target.setXmpp(new XmppEntity(record.getValue(U.USERNAME)));
                    target.setVip(record.getValue("is_vip") != 0);
                    target.setOnline(activityService.isOnline(targetId));

                    MutualMatchEntity mutualMatch = roster.getMutualMatch();
                    mutualMatch.setMatched(record.getValue("is_matched") != 0);
                    mutualMatch.setMatchTime((Date) record.getValue("matched_time"));
                    mutualMatch.setChatFlag(record.getValue("has_notchat_match") == 1);
                }

                return rosters;
            }
        }, userId);
    }

    public void readMessage(long userId, long targetId) throws Exception
    {
        this.execute(new IExecute()
        {
            @Override
            public void execute(DSLContext context, Object... params) throws Exception
            {
                // params
                long userId = (long) params[0];
                long targetId = (long) params[1];

                // table alias
                ChatRosters CR = Tables.CHAT_ROSTERS;
                NotchatMutualmatch NCMM = Tables.NOTCHAT_MUTUALMATCH;

                // reset unread info
                context
                    .update(CR)
                    .set(CR.UNREAD_NUM, (short) 0)
                    .where(CR.USER_ID.eq(userId), CR.TARGET_ID.eq(targetId))
                    .execute();
            }
        }, userId, targetId);
    }

    public void sendMessage(long userId, long targetId, String msg) throws Exception
    {
        this.execute(new IExecute()
        {
            @Override
            public void execute(DSLContext context, Object... params) throws Exception
            {
                // params
                long userId = (long) params[0];
                long targetId = (long) params[1];
                String msg = (String) params[2];

                // table alias
                ChatRosters CR = Tables.CHAT_ROSTERS;
                Events E = Tables.EVENTS;

                // update rosters
                context
                    .update(CR)
                    .set(CR.UNREAD_NUM, CR.UNREAD_NUM.add(1))
                    .set(CR.LAST_MESSAGE, msg)
                    .set(CR.LAST_MESSAGE_TIME, DSL.currentTimestamp())
                    .set(CR.LAST_ACTIVE_TIME, DSL.currentTimestamp())
                    .where(CR.USER_ID.eq(targetId), CR.TARGET_ID.eq(userId))
                    .execute();
                context
                    .update(CR)
                    .set(CR.LAST_MESSAGE, msg)
                    .set(CR.LAST_MESSAGE_TIME, DSL.currentTimestamp())
                    .set(CR.LAST_ACTIVE_TIME, DSL.currentTimestamp())
                    .where(CR.USER_ID.eq(userId), CR.TARGET_ID.eq(targetId))
                    .execute();

                // add event
                Record record = context
                    .select(E.LIST_MESSAGE)
                    .from(E)
                    .where(E.TARGET_ID.eq(targetId), E.STATUS.eq(0))
                    .limit(1)
                    .fetchOne();
                if(record == null)
                {
                    String jsonMessages= String.format("[\"%d\"]", targetId);
                    context
                        .insertInto(E, E.TARGET_ID, E.LIST_MESSAGE, E.STATUS)
                        .values(targetId, jsonMessages, 0)
                        .execute();
                }
                else
                {
                    String jsonMessages = record.getValue(E.LIST_MESSAGE);
                    String newJsonMessages = null;
                    if(jsonMessages == null || jsonMessages.equals("[]"))
                    {
                        newJsonMessages = String.format("[\"%d\"]", targetId);
                    }
                    else if(!jsonMessages.contains(String.valueOf(targetId)))
                    {
                        newJsonMessages = jsonMessages.replaceAll("]", String.format(",%d]", targetId));
                    }
                    if(newJsonMessages != null)
                    {
                        context
                            .update(E)
                            .set(E.LIST_MESSAGE, jsonMessages)
                            .where(E.TARGET_ID.eq(targetId), E.STATUS.eq(0))
                            .execute();
                    }
                }
            }
        }, userId, targetId, msg);
    }
}
